﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web.Mvc;
using System.Web.UI;
using System.IO;

namespace CodeCamp.BL.UI
{
	/// <summary>
	/// A filter function to apply to each menu item.
	/// </summary>
	/// <remarks>This function is expected to return false (do not filter) if an item is to be shown; true otherwise;</remarks>
	public delegate bool MenuItemFilter(MvcMenuItemAttribute item);

	public static class MvcMenu
	{

		private static List<MvcMenuItemAttribute> _MenuItems = new List<MvcMenuItemAttribute>();

		/// <summary>
		/// An optional fitler function to apply to the menu.
		/// </summary>
		/// <remarks>Use this to enable filtering based on anything you need (security role, context etc)</remarks>
		public static MenuItemFilter ItemFilter;

		#region Rendering

		public static void ShowMenu(this TextWriter output)
		{
			var writer = new HtmlTextWriter(output);

			renderHierarchy(writer, _MenuItems, null);
		}

		public static void ShowBreadCrumb(this TextWriter output, Uri currentUri)
		{
			var writer = new HtmlTextWriter(output);
			string currentLink = "/" + currentUri.GetComponents(UriComponents.Path, UriFormat.Unescaped);

			var menuItem = _MenuItems.FirstOrDefault(m => m.Link.Equals(currentLink, StringComparison.CurrentCultureIgnoreCase));
			if (menuItem != null)
			{
				renderBreadCrumb(writer, _MenuItems, menuItem);
			}
		}

		private static void renderBreadCrumb(HtmlTextWriter writer, List<MvcMenuItemAttribute> menuItems, MvcMenuItemAttribute current)
		{
			if (current == null)
			{
				return;
			}
			var parent = current.ParentItem;
			renderBreadCrumb(writer, menuItems, parent);
			writer.Write(current.MenuText);
			writer.Write(" / ");

		}


		static void renderHierarchy(HtmlTextWriter writer, List<MvcMenuItemAttribute> hierarchy, MvcMenuItemAttribute root)
		{
			if (!hierarchy.Any(i => i.ParentItem == root)) return;

			writer.RenderBeginTag(HtmlTextWriterTag.Ul);
			foreach (var current in hierarchy.Where(element => element.ParentItem == root).OrderBy(i => i.Order))
			{
				if (ItemFilter == null || ItemFilter(current))
				{

					writer.RenderBeginTag(HtmlTextWriterTag.Li);
					writer.AddAttribute(HtmlTextWriterAttribute.Href, current.Link);
					writer.AddAttribute(HtmlTextWriterAttribute.Alt, current.MenuText);
					writer.RenderBeginTag(HtmlTextWriterTag.A);
					writer.WriteEncodedText(current.MenuText);
					writer.RenderEndTag(); // link
					renderHierarchy(writer, hierarchy, current);
					writer.RenderEndTag(); // li
				}
			}
			writer.RenderEndTag(); // ul
		}

		#endregion

		#region Menu Item Discovery

		public static void RegisterMenuItems(Type mvcApplicationType)
		{
			RegisterMenuItems(ListMenuItems(Assembly.GetAssembly(mvcApplicationType)));
		}

		public static void RegisterMenuItems(List<MvcMenuItemAttribute> items)
		{
			_MenuItems = items;
			_MenuItems.ForEach(i => i.ParentItem =
									items.FirstOrDefault(p =>
														 String.Equals(p.Link, i.ParentLink, StringComparison.InvariantCultureIgnoreCase)));
		}

		public static List<MvcMenuItemAttribute> ListMenuItems(Assembly assembly)
		{
			var result = new List<MvcMenuItemAttribute>();
			foreach (var type in assembly.GetTypes())
			{
				if (!type.IsSubclassOf(typeof(Controller)))
				{
					continue;
				}
				foreach (var method in type.GetMethods())
				{
					var items = method.GetCustomAttributes(typeof(MvcMenuItemAttribute), false) as MvcMenuItemAttribute[];

					if (items == null)
					{
						continue;
					}
					foreach (var item in items)
					{
						if (String.IsNullOrEmpty(item.Controller))
						{
							item.Controller = type.Name.Substring(0, type.Name.Length - "Controller".Length);
						}
						if (String.IsNullOrEmpty(item.Action))
						{
							item.Action = method.Name;
						}
						result.Add(item);
					}
				}
			}

			return result.OrderBy(i => i.Order).ToList();
		}

		#endregion
	}
}
